#include "factory.h"

int exit_fds[2];
//退出机制信号处理函数
void sig_exit_func(int signum)
{
    write(exit_fds[1],&signum,1);
}

int main(int argc,char** argv)
{
    if(argc!=3)
    {
        printf("/thread_pool_server IP PORT\n");
        return -1;
    }
    pipe(exit_fds);
    if(fork())//父进程用来协助完成退出机制
    {
        close(exit_fds[0]);
        signal(SIGUSR1,sig_exit_func);//将10号信号传给信号处理函数
        wait(NULL);
       return 0;
    }
    close(exit_fds[1]);
    Factory_t f;//主数据结构
    int thread_num=THREAD_NUM;//子线程数量
    int capacity=CAPACITY;//队列容量
    factory_init(&f,thread_num,capacity);//线程池初始化
    factory_start(&f,thread_num);//启动线程池
    int socket_fd;
    tcp_init(&socket_fd,argv[1],argv[2]);
    //接收请求，加锁，放入队列，解锁，signal
    int new_fd;
    struct sockaddr_in client_addr;
    Client_State_t* pnew;
    train_state_t* ct;
    pQue_t pq=&f.que;//让一个队列指针指向队列
    pNode_t pnew_task;
    cque client_que;
    //epoll注册监控socket_fd,exit_fd[0]
    int epfd=epoll_create(1);
    struct epoll_event evs[5];
    epoll_add(epfd,exit_fds[0]);
    epoll_add(epfd,socket_fd);
    int ready_fd_num,i,j,ret;
    usraddr usr;
    char salt[30]={0};
    int salt_len=sizeof(salt);
    int pwdret,data_len;
    while(1)
    {
        ready_fd_num=epoll_wait(epfd,evs,5,-1);//等待描述符就绪，哪个描述符就绪，epoll就会返回
        for(i=0;i<ready_fd_num;i++)
        {
            if(evs[i].data.fd==socket_fd)//如果有客户端连接了，判断是登录还是注册
            {
                bzero(&client_addr,sizeof(client_addr));//清空
                socklen_t addr_len=sizeof(client_addr);
                new_fd=accept(socket_fd,(struct sockaddr*)&client_addr,&addr_len);
                ERROR_CHECK(new_fd,-1,"accept");
                if(new_fd!=-1)
                {
                    printf("%s %d connect\n\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
                }    
                else
                {
                    printf("%d Connect fail!\n",new_fd);
                    continue;
                }
                //登录或者失败的话，都从这里重新运行
fail_label:
                bzero(&ct,sizeof(train_state_t));
                ret=recv_n(new_fd,&data_len,sizeof(int));//接命令长度
                SELFFUNC_ERR_CHECK(ret,"recv_n");
                ret=recv_n(new_fd,&ct,4+data_len);//接命令内容
                SELFFUNC_ERR_CHECK(ret,"recv_n");
                ct->cmd_type = cmd_type_func(ct->cmd);
                if(ct->cmd_type==TOKEN)//TOKEN验证
                {
TOKEN:              ret=server_token(new_fd);
                    if(ret==-1)
                    {
                        goto fail_label;
                    }
                }  
                else if(ct->cmd_type==LOGIN)
                {
                    memset(&usr,0,sizeof(usraddr));
                    ret=recv_n(new_fd,&usr.data_len,sizeof(int));//接用户名长度 
                    SELFFUNC_ERR_CHECK(ret,"recv_n");
                    ret=recv_n(new_fd,&usr.usrA,4+usr.data_len);//接用户名内容
                    SELFFUNC_ERR_CHECK(ret,"recv_n");
                    printf("%s come!\n",usr.usrA);
LOGIN:              ret=server_login(new_fd,ct,usr,salt,&client_que);
                    if(ret==-1)
                    {
                        goto fail_label;
                    }                   
                }
                else if(ct->cmd_type==REGISTER)
                {
REGISTER:             ret=server_register(new_fd,ct,usr,salt,&client_que);
                    if(ret==-1)
                    {
                        goto fail_label;
                    }                   
                }
                else if(ct->cmd_type==QUIT)
                {
                    printf("client quit.\n\n");
                    close(new_fd);
                    break;
                }
                else if(ct->cmd_type==FAULTY)
                {
FAULT:              printf("fault command ignored.\n");
                    goto fail_label;
                }
                //登陆成功，监视对应new_fd
                epoll_add(epfd,new_fd);
            }
            else if(evs[i].data.fd==new_fd)//如果new_fd可读
            {
                //从头部遍历开始用户服务队列，如果发来数据的new_fd在队列中找到了，用p指向该client结点
                pcnode p=client_que.que_head;
                while(1)
                {
                    if(p->client.new_fd==evs[i].data.fd)
                    {
                        break;
                    }
                    p=p->next_client;
                }
                //此时p指向第一个new_fd等于发来数据的new_fd的client结点,也就是要服务的client结点
                bzero(&ct,sizeof(train_state_t));
                ret=recv_n(new_fd,&data_len,sizeof(int));//接命令长度
                SELFFUNC_ERR_CHECK(ret,"recv_n");
                ret=recv_n(new_fd,&ct,4+data_len);//接命令内容
                SELFFUNC_ERR_CHECK(ret,"recv_n");
                ct->cmd_type = cmd_type_func(ct->cmd);
                if(ct->cmd_type==CD)
                {
                    server_cd(p,ct);
                }
                else if(ct->cmd_type==LS)
                {
                    server_ls(p,ct);
                }
                else if(ct->cmd_type==PWD)
                {
                    server_pwd(p,ct);
                }
                else if(ct->cmd_type==REMOVE)
                {
                    server_remove(p,ct);
                }
                else if(ct->cmd_type==PUTS||GETS)
                {
                    pnew=(Client_State_t*)calloc(1,sizeof(Client_State_t));//申请空间
                    //pnew放入队列
                    pnew->new_fd=new_fd;
                    strcpy(pnew->ip,argv[1]);
                    strcpy(pnew->port,argv[2]);
                    pthread_mutex_lock(&pq->mutex);//加锁
                    que_set(pq,pnew);//放入队列
                    pthread_mutex_unlock(&pq->mutex);//解锁
                    pthread_cond_signal(&f.cond);//使条件成立
                }
            }
            if(evs[i].data.fd==exit_fds[0])//线程池要退出了
            {
                for(j=0;j<thread_num;j++)//给每一个子线程发送cancel信号
                {
                    pthread_cancel(f.pthid[j]);
                    printf("thread %d cancel success\n",getpid());
                }
                for(j=0;j<thread_num;j++)//等待每一个子线程
                {
                    pthread_join(f.pthid[j],NULL);
                }
            }
        }
    }
    close(socket_fd);//关闭对应的描述符
    close(new_fd);
    return 0;
}
